#include <stdlib.h>
#include <string.h>
#include "redcodeobject.h"
#include "bytecode_buffer.h"

void bytecode_buffer_ref(bytecode_buffer_t *self)
{
	if (self)
		self->ref++;
}

void bytecode_buffer_unref(bytecode_buffer_t *self)
{
	if (self && --(self->ref) == 0)
	{
		for (int i = 0; i < self->count; i++)
			bytecode_release(&(self->bucket[i]));

		free(self->bucket);
		free(self);
	}
}

void bytecode_buffer_release(bytecode_buffer_t **self)
{
	if (self)
	{
		bytecode_buffer_unref(*self);
		*self = NULL;
	}
}

bytecode_buffer_t *bytecode_buffer_create(void)
{
	bytecode_buffer_t *result = malloc(sizeof(bytecode_buffer_t));

	result->pc = 0;
	result->ref = 1;
	result->size = 16;
	result->count = 0;
	result->bucket = malloc(sizeof(bytecode_t *) * result->size);
	return result;
}

void bytecode_buffer_free(bytecode_t *code, int size)
{
	for (int i = 0; i < size; i++)
		bytecode_clear(&code[i]);

	free(code);
}

bytecode_t *bytecode_buffer_emit(bytecode_buffer_t *self, opcode_t opcode, int lineno, int position)
{
	bytecode_t *bytecode = bytecode_create(opcode, lineno, position);

	if (self->count == self->size)
	{
		self->size *= 2;
		self->bucket = realloc(self->bucket, sizeof(bytecode_t *) * self->size);
	}

	self->count++;
	self->bucket[self->pc++] = bytecode;
	return bytecode;
}

RedObject *bytecode_buffer_assemble(bytecode_buffer_t *self, rstring_t *fname, rstring_t *source, RedObject *module)
{
	bytecode_t *code = malloc(sizeof(bytecode_t) * self->count);

	for (int i = 0; i < self->count; i++)
	{
		code[i].ref = 0;
		code[i].opcode = self->bucket[i]->opcode;
		code[i].lineno = self->bucket[i]->lineno;
		code[i].position = self->bucket[i]->position;
		code[i].at_offset = self->bucket[i]->at_offset;
		code[i].at_object = self->bucket[i]->at_object;
	}

	/* wrap bytecodes with RedCodeObject */
	RedObject *result = RedCode_WrapBytecode(fname, code, self->count);

	/* load file name and source code into code object */
	RedCode_LoadSource(result, source, module);
	return result;
}
